/*
 *   落地方式：输出控制台/指定文件/滚动文件（按照大小或者时间滚动）
 *
 */
#ifndef __M_SINK_H__
#define __M_SINK_H__
#include "formatter.hpp"
#include "level.hpp"
#include "message.hpp"
#include<fstream>
#include <time.h>
namespace zxlog
{
    class LogSink
    {
    public:
        using ptr = std::shared_ptr<LogSink>;
        virtual ~LogSink(){};
        virtual void log(const std::string &data, size_t len) = 0;
    };
    class StdoutSink : public LogSink
    {
    public:
        void log(const std::string &data, size_t len)override
        {
            std::cout.write(data.c_str(), len);
        }
    };
    class FileSink : public LogSink
    {
    private:
        std::string _filename;
        std::ofstream _ofs;

    public:
        FileSink(const std::string &filename) : _filename(filename)
        {
            // file::create_directory(_filename);//不能直接使用文件名，会根据文件名创建目录的，需要传递路径                
            file::create_directory(file::path(_filename));                      // 创建文件
            _ofs.open(_filename.c_str(), std::ios::binary | std::ios::out); // 打开文件
            assert(_ofs.is_open());                                 // 判断文件是否打开成功
        }
        void log(const std::string &data, size_t len)override
        {
            _ofs.write(data.c_str(), len);
            if (!_ofs.good())
                std::cerr << "FileSink: 日志输出失败！" << std::endl;
        }
    };
    class RollSink : public LogSink
    {
    public:
        RollSink(const std::string &basename, const size_t max_fsize)
            : _basename(basename), _max_fsize(max_fsize), _cur_fsize(0)
        {
            file::create_directory(file::path(_basename));
            std::string filename = CreatFileName();
            _ofs.open(filename.c_str(), std::ios::binary | std::ios::out);
            assert(_ofs.is_open());
        }
        void log(const std::string &data, size_t len)override
        {
            InitFileSize(len);
            _ofs.write(data.c_str(), len);
            if (!_ofs.good())
                std::cerr << "RollSink: 日志文件输出失败！" << std::endl;
            _cur_fsize += len;
        }

    public:
        void InitFileSize(size_t len)
        {
            // 关闭当前文件，打开一个新的文件
            if (len + _cur_fsize > _max_fsize)
            {
                // std::cout<<"count:"<<count<<std::endl;
                _ofs.close();
                std::string filename = CreatFileName();
                // std::cout<<"InitFileSize filename:"<<filename<<std::endl;
                _ofs.open(filename, std::ios::binary | std::ios::out);
                assert(_ofs.is_open());
                _cur_fsize = 0;
            }
        }
        std::string CreatFileName()
        {
            // 根据时间为不同文件命名
            time_t t = util::now(); // 获取当前时间
            struct tm lt;
            localtime_r(&t, &lt);
            std::stringstream ss;
            ss << _basename;
            ss << lt.tm_year + 1900;
            ss << lt.tm_mon + 1;
            ss << lt.tm_mday;
            ss << lt.tm_hour;
            ss << lt.tm_min;
            ss << lt.tm_sec;
            ss << count++;
            ss << ".log";
            return ss.str();
        }

    private:
        std::string _basename;
        std::ofstream _ofs;
        size_t _max_fsize;
        size_t _cur_fsize;
        int count = 0;
    };
    class SinkFactory{
        public:
     template<class Type,class ... Args>
     static LogSink::ptr create(Args&& ... args)
     {
         return std::make_shared<Type>(std::forward<Args>(args)...);
     }
    };
}
#endif
